package com.ktwlsoft.fundmanage.fundFlow.service;

import com.ktwlsoft.fundmanage.domain.business.BaseBusiness;
import com.ktwlsoft.fundmanage.payload.BaseResult;
import com.ktwlsoft.fundmanage.payload.ResultType;
import com.ktwlsoft.fundmanage.payload.exceptions.FlowTaskMoreOrNoneException;
import com.ktwlsoft.fundmanage.payload.exceptions.TaskNotMatchRoleException;
import com.ktwlsoft.fundmanage.service.CandidateService;
import com.ktwlsoft.fundmanage.service.FInterface.ICandidate;
import com.ktwlsoft.fundmanage.utils.UserUtils;
import org.activiti.engine.*;
import org.activiti.engine.form.StartFormData;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public abstract class BaseFlow extends BaseBusiness {

    protected final ICandidate candidate;


    protected final RuntimeService runtimeService;


    protected final TaskService taskService;


    protected final ProcessEngine processEngine;


    protected final FormService formService;


    protected final RepositoryService repositoryService;

    protected BaseFlow(CandidateService candidate, RuntimeService runtimeService, TaskService taskService, ProcessEngine processEngine, FormService formService, RepositoryService repositoryService) {
        this.candidate = candidate;
        this.runtimeService = runtimeService;
        this.taskService = taskService;
        this.processEngine = processEngine;
        this.formService = formService;
        this.repositoryService = repositoryService;
    }

    @Transactional(rollbackOn = Exception.class)
    public boolean checkOperator() {
        if (candidate.getClerk().size() > 0 && candidate.getAccountant().size() > 0 && candidate.getAuditor().size() > 0 && candidate.getCashier().size() > 0 && candidate.getAuditorAndCashier().size() > 0) {
            return true;
        } else {
            return false;
        }
    }

    @Transactional(rollbackOn = Exception.class)
    public ProcessInstance startProcess(String processKey) {
        return runtimeService.startProcessInstanceByKey(processKey);
    }

    public StartFormData getStartFormData(String processDefinedId) {
        return formService.getStartFormData(processDefinedId);
    }

    @Transactional(rollbackOn = Exception.class)
    public ProcessInstance startProcess(String processKey, Map<String, Object> varaibles) {
        return runtimeService.startProcessInstanceByKey(processKey, varaibles);
    }

    @Transactional(rollbackOn = Exception.class)
    public List<Task> getTasks(String assignee) {
        return taskService.createTaskQuery().taskCandidateOrAssigned(assignee).list();
    }

    @Transactional(rollbackOn = Exception.class)
    public Task getAssigneeTasks(String assignee, String task, String instanceId) throws FlowTaskMoreOrNoneException {
        List<Task> tasks = taskService.createTaskQuery().taskCandidateOrAssigned(assignee).taskName(task).processInstanceId(instanceId).list();
        if (tasks.size() > 1) {
            throw new FlowTaskMoreOrNoneException("Too much tasks!");
        } else if (tasks.size() == 0) {
            throw new FlowTaskMoreOrNoneException("No Task!");
        } else {
            return tasks.get(0);
        }
    }

    @Transactional(rollbackOn = Exception.class)
    public Task getTask(String task, String instanceId) throws FlowTaskMoreOrNoneException {
        List<Task> tasks = taskService.createTaskQuery().taskName(task).processInstanceId(instanceId).list();
        if (tasks.size() > 1) {
            throw new FlowTaskMoreOrNoneException("Too much tasks!");
        } else if (tasks.size() == 0) {
            throw new FlowTaskMoreOrNoneException("No Task!");
        } else {
            return tasks.get(0);
        }
    }

    @Transactional(rollbackOn = Exception.class)
    public Task getRoleTask(List<String> userRoles, String taskName, String instance) throws FlowTaskMoreOrNoneException {
        if (userRoles.size() > 0) {
            List<Task> tasks = new ArrayList<>();
            for (String role :
                    userRoles) {
                tasks.addAll(taskService.createTaskQuery().taskCandidateGroup(role).taskName(taskName).processInstanceId(instance).list());
                if (tasks.size() > 0) {
                    break;
                }
            }

//            tasks = taskService.createTaskQuery().taskCandidateGroup(userRoles.get(0)).taskName(taskName).processInstanceId(instance).list();
            if (tasks.size() > 1) {
                throw new FlowTaskMoreOrNoneException("Too much tasks!");
            } else if (tasks.size() == 0) {
                throw new FlowTaskMoreOrNoneException("No Task!");
            } else {
                return tasks.get(0);
            }
        } else {
            throw new TaskNotMatchRoleException("User can't operate this task!");
        }
    }



    @Transactional(rollbackOn = Exception.class)
    public List<Task> getAssigneeTasks(TaskType taskType, String name) {
        if (taskType.equals(TaskType.GROUP)) {
            return taskService.createTaskQuery().taskCandidateGroup(name).list();
        } else {
            return taskService.createTaskQuery().taskCandidateUser(name).list();
        }
    }

    @Transactional(rollbackOn = Exception.class)
    public void completeTask(String taskId, Map<String, Object> variable) {
        if (variable == null) {
            taskService.complete(taskId);
        } else {
            taskService.complete(taskId, variable);
        }
    }

    public BaseResult<?> finishTask(String taskName, String instanceId, Map<String, Object> variable) {
        try {
            List<String> roles = UserUtils.getUser().getRoles();
            Task task = getRoleTask(roles, taskName, instanceId);
            taskService.complete(task.getId(), variable);
            return BaseResult.ok("Mission Completed!");
        } catch (FlowTaskMoreOrNoneException e) {
            return BaseResult.body(e.getMessage(), ResultType.BUSINESS_ERROR);
        }
    }

    @Transactional(rollbackOn = Exception.class)
    public void deleteTask(String processId, String reason) {
        runtimeService.deleteProcessInstance(processId, reason);
    }

    public enum TaskType {
        /**
         * Group
         */
        GROUP,
        /**
         * USER
         */
        USER
    }
}
